Skip to content

feat(expert): plan mode — propose a plan before acting (#408, #409)#7635

Merged
cstns merged 17 commits into
mainfrom
feat/408-expert-plan-mode
Jul 2, 2026
Merged

feat(expert): plan mode — propose a plan before acting (#408, #409)#7635
cstns merged 17 commits into
mainfrom
feat/408-expert-plan-mode

Conversation

@andypalmi

@andypalmi andypalmi commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Frontend for Expert plan mode (FlowFuse/product#408, FlowFuse/product#409): the Expert proposes a plan before making any changes, and acts only once the user approves it.

  • Plan mode toggle: an always-visible toggle chip in the composer. When enabled, the Expert proposes a plan instead of acting; the flag is shipped to the agent via the expert context.
  • Plan card: the proposed plan renders as its own card (Markdown via RichContent) with four actions:
    • Approve — exits plan mode and proceeds with the plan (a follow-up acting turn).
    • Edit — loads the plan markdown into the composer for direct editing and resubmission.
    • Request changes — focuses an empty composer to describe a change in words; the agent re-proposes an updated plan.
    • Reject — abandons the plan.
  • A plan card from a past turn is disabled once a newer message arrives (shared with the questions cards).
  • Fixes a missing-prop console warning: the plan card now passes the message/answer uuids that RichContent requires.

Stacking

This PR was stacked on #7556 (clarifying questions), which has now merged. It now targets main directly and the diff shown here is plan-mode only.

Testing

  • npm run lint, npm run build and npm run test:unit:frontend (570 tests) all pass.
  • Manual: toggling plan mode, and Approve / Edit / Request changes / Reject on the plan card; no console warning on plan render.

Closes:

  • FlowFuse/product#406
  • FlowFuse/product#408
  • FlowFuse/product#409
  • FlowFuse/product#410

andypalmi added 3 commits June 29, 2026 19:41
…crash

Add an ErrorBoundary and wrap each answer item in it, so a failure in one
section degrades only that section instead of blanking the whole message.
Also guard the optional streamable chain in StandardResourceCard that could
throw on a null value and take down the message.
)

The Expert can ask 1-4 clarifying questions in a single turn, each rendered as
its own single- or multi-select option card; all answers are collected before
the turn is submitted. Answered cards can be edited and resubmitted, and a card
from a past turn is disabled once a newer message arrives.

Adds a follow-up-questions cadence setting (all at once vs one at a time) in the
composer settings menu, shipped to the agent via the expert context.
Add an always-visible Plan mode toggle to the composer. When enabled, the Expert
proposes a plan instead of making changes, rendered as a plan card with Approve,
Edit, Request changes and Reject actions:

- Approve exits plan mode and proceeds with the plan.
- Edit loads the plan markdown into the composer for direct editing.
- Request changes focuses an empty composer to describe a change in words.
- Reject abandons the plan.

The plan card renders its markdown through RichContent (passing the message and
answer uuids it requires), and reuses the composer's pending-input and auto-grow
behaviour. Plan mode and the approval signal are shipped to the agent via the
expert context.
@codecov

codecov Bot commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.35%. Comparing base (f6ceec4) to head (13d00b9).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #7635   +/-   ##
=======================================
  Coverage   75.35%   75.35%           
=======================================
  Files         425      425           
  Lines       22487    22487           
  Branches     5930     5930           
=======================================
+ Hits        16944    16945    +1     
+ Misses       5543     5542    -1     
Flag Coverage Δ
backend 75.35% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Plan mode is only meaningful inside the instance/device editor for now,
so gate the composer toggle on immersive mode and force the persisted
planMode off whenever the user is outside immersive (including on load),
preventing a stale value from being sent in non-immersive contexts.
andypalmi added 4 commits June 30, 2026 14:34
- Guard the optional streamable chain in FlowResourceCard directly instead
  of relying on a render boundary to mask the throw. Reduce ErrorBoundary to
  a single last-resort backstop per answer item in AiMessage; drop the
  per-section boundary wrappers in AnswerWrapper.
- Rewrite QuestionsList on top of the existing ff-radio-group (single-select)
  and ff-checkbox (multi-select) components so options look like standard,
  clickable form controls and stay consistent with the rest of the app.
- Replace the imperative growComposerToContent DOM measuring with CSS
  field-sizing on the textarea; drop the manual reflows and the auto-grown
  flag. The composer auto-sizes to content and pins to an explicit height
  only after a drag-resize.
Per review: the local-catch pattern was the only one in the frontend; the
rest of the app leans on the global app.config.errorHandler. The real throws
are now guarded at their source (the optional streamable chains in the
resource cards), so the boundary was redundant. Remove it entirely and let
genuinely unexpected render errors surface through the global handler like
everywhere else.
Replace the composer kebab menu with a settings gear that opens an
ff-dialog. The follow-up-questions cadence control now lives in the
dialog as an ff-radio-group, with a FormHeading per section so the
panel can grow as more settings are added.
…n-mode

# Conflicts:
#	frontend/src/components/expert/components/ExpertChatInput.vue
#	frontend/src/components/expert/components/messages/components/AnswerWrapper.vue
andypalmi added 2 commits June 30, 2026 17:17
handleMessageResponse lived in the composer's send handler, so a reply to a
query sent from the questions card was fetched but never rendered over HTTP
(without comms-beta the reply only renders via the MQTT push handler). Fold
the response handling into handleQuery so every entry point (composer and the
question/plan cards) renders the reply without re-implementing it.
…n-mode

# Conflicts:
#	frontend/src/components/expert/components/ExpertChatInput.vue
#	frontend/src/stores/product-expert.js

@n-lark n-lark left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey flagged a couple things. I wasn't able to test this since we don't have a staging env for non mainline PRs. Lmk when this is pointed at main and I will test. nvm I am wrong, will test now as well and update.

One general note - there are a lot of code comments here, and Serban got on me for this exact thing too so you're not alone. When they get this verbose they kinda make the code harder to read. My rule of thumb is if you can figure it out from reading the code, don't comment it. Might be worth dropping into a CLAUDE.md.

Comment thread frontend/src/stores/product-expert.js Outdated
Comment thread frontend/src/stores/product-expert.js Outdated
Comment thread frontend/src/stores/product-expert.js Outdated
@n-lark

n-lark commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

UI issue here - besides that functionality seems solid.

  1. Sending a plan to be edited exceeds the height of the expert chat, we probably need a max height and a scroll here.
Screenshot 2026-06-30 at 11 07 05 AM

Comment thread frontend/src/components/expert/components/ExpertChatInput.vue
Base automatically changed from feat/407-expert-quick-replies to main July 1, 2026 09:15
…mode

# Conflicts:
#	frontend/src/components/expert/components/ExpertChatInput.vue
#	frontend/src/components/expert/components/messages/components/AnswerWrapper.vue
#	frontend/src/stores/context.js
#	frontend/src/stores/product-expert.js
andypalmi added 2 commits July 1, 2026 11:36
- Remove the unused contextOverrides param threaded through handleQuery/sendQuery.
- Stop persisting planMode so approving a plan (which turns it off) no longer
  wipes the user's preference; it defaults off per session.
- Replace the planChangeRequest/composerReset counters with a single
  composerCommand consumed and cleared like pendingInput, collapsing two
  composer watchers into one.
- Give the composer textarea min-height: 0 so a long plan loaded via Edit
  manually scrolls instead of overflowing the chat.
- Add a size="small" variant to the shared ff-toggle-switch and drop the
  fragile :deep sizing overrides. The prop defaults to "normal" (an inert
  class) so existing consumers are unchanged.
- Trim verbose comments across the plan-mode files.
Drop the immersive-only gate on the plan mode toggle and the watcher that
forced it off when leaving the editor, so plan mode is offered in both the
FlowFuse app and the instance/device editor.
@andypalmi

Copy link
Copy Markdown
Contributor Author

One general note - there are a lot of code comments here, and Serban got on me for this exact thing too so you're not alone. When they get this verbose they kinda make the code harder to read. My rule of thumb is if you can figure it out from reading the code, don't comment it. Might be worth dropping into a CLAUDE.md.

Fair, and noted. Did a pass to cut the comments back to just the non-obvious stuff and left the code to speak for itself where it can.

The min-height: 0 needed for the composer to scroll instead of grow was
only on the textarea, not its flex parent. Add it to .input-wrapper so the
whole flex chain can shrink and a long plan loaded via Edit scrolls within
the capped composer.
@andypalmi

Copy link
Copy Markdown
Contributor Author

Pushed the review fixes:

  • Removed the dead contextOverrides param.
  • Stopped persisting planMode, so approving a plan no longer changes the saved preference.
  • Replaced the composer counters with a single composerCommand modelled on pendingInput.
  • Added a size variant to ToggleSwitch instead of :deep-resizing it.
  • Trimmed the verbose comments.
  • Capped the composer height when a long plan is loaded via "Edit" so it scrolls instead of growing (the min-height: 0 was missing on the textarea's flex parent).

One change beyond the review: I removed the logic that limited plan mode to the immersive editor, so it's now available across the app rather than only inside an instance/device editor.

Plan mode now adapts to where the user is: it uses the scope already sent in the Expert context (immersive for an instance/device editor, ff-app otherwise) to change what the agent proposes and what it can act on depending on whether it's in the FlowFuse app or the editor. This is the groundwork for the follow-up PR that adds the platform automation tools and human-in-the-loop approvals across both the platform tools and the flow-building tools. See #7639

Here is a short recording of the plan-mode flow in action:

Screen.Recording.2026-07-01.at.12.40.43.mov

@n-lark would you mind giving it a last look?

@andypalmi

andypalmi commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

PR was approved but we will need to wait for agent deployment to production before merging, otherwise users will see plan mode but will not be working until agent is updated

cc: @Steve-Mcl @cstns @allthedoll

@andypalmi andypalmi requested a review from cstns July 2, 2026 08:02
…mode

# Conflicts:
#	frontend/src/stores/context.js
@cstns cstns merged commit 676f620 into main Jul 2, 2026
29 checks passed
@cstns cstns deleted the feat/408-expert-plan-mode branch July 2, 2026 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants